package com.kimehwa.studyboot.basejava.lambda;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTestBase {
    private static List<Person> list;

    static {
        list = new ArrayList<>();
        list.add(new Person("i", 18, "杭州", 999.9));
        list.add(new Person("am", 19, "温州", 777.7));
        list.add(new Person("iron", 21, "杭州", 888.8));
        list.add(new Person("man", 17, "宁波", 888.8));
    }

    public static void main(String[] args) {
        // 我们先学中间操作

        // 1.先获取流（不用管其他乱七八糟的创建方式，记住这一个就能应付95%的场景）
        Stream<Person> stream = list.stream();
        // 2.过滤年纪大于18岁的
        Stream<Person> filteredByAgeStream = stream.filter(person -> person.getAge() > 18);
        // 3.只要名字，不需要整个Person对象（为什么在这个案例中，filter只能用Lambda，map却可以用方法引用？）
        Stream<String> nameStream = filteredByAgeStream.map(Person::getName);
        // 4.现在返回值是Stream<String>，没法直接使用，帮我收集成List<String>
        List<String> nameList = nameStream.collect(Collectors.toList());

        // 现在还对collect()为什么传递Collectors.toList()感到懵逼吗？
    }

    @Test
    void  testSort(){
        // JDK8之前：Collections工具类+匿名内部类。Collections类似于Arrays工具类，我经常用Arrays.asList(）
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getName().length()-p2.getName().length();
            }
        });

        // JDK8之前：List本身也实现了sort()
        list.sort(new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getName().length()-p2.getName().length();
            }
        });

        // JDK8之后：Lambda传参给Comparator接口，其实就是实现Comparator#compare()。注意，equals()是Object的，不妨碍
        list.sort((p1,p2)->p1.getName().length()-p2.getName().length());

        // JDK8之后：使用JDK1.8为Comparator接口新增的comparing()方法
        list.sort(Comparator.comparingInt(p -> p.getName().length()));

    }

    @Test
    void testStreamSort(){
        // 直接链式操作
        List<String> nameList = list.stream()
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println(nameList);

        // 我想按姓名长度排序
        List<String> sortedNameList = list.stream()
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .sorted()
                .collect(Collectors.toList());
        System.out.println(sortedNameList);
        // 你：我擦，说好的排序呢？

        // Stream：别扯淡，你告诉我排序规则了吗？（默认自然排序）

        // 明白了，那就按照长度倒序吧（注意细节啊，str2-str1才是倒序）
        List<String> realSortedNameList = list.stream()
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .sorted((str1, str2) -> str2.length() - str1.length())
                .collect(Collectors.toList());
        System.out.println(realSortedNameList);

        // 优化一下：我记得在之前那张很大的思维导图上看到过，sorted()有重载方法，是sorted(Comparator)
        // 上面Lambda其实就是调用sorted(Comparator)，用Lambda给Comparator接口赋值
        // 但Comparator还供了一些方法，能返回Comparator实例
        List<String> optimizeNameList = list.stream()
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
        System.out.println(optimizeNameList);

        // 但上面的有点投机取巧，来个正常点的，使用Comparator.comparing()
        List<String> result1 = list.stream()
                .filter(person -> person.getAge() > 18)
                .map(Person::getName)
                .sorted(Comparator.comparing(t -> t, (str1, str2) -> str2.length() - str1.length()))
                .collect(Collectors.toList());
        System.out.println(result1);

        // 我去，更麻烦了！！
        // 不急，我们先来了解上面案例中Comparator的两个参数
        // 第一个是Function映射，就是指定要排序的字段，由于经过上一步map操作，已经是name了，就不需要映射了，所以是t->t
        // 第二个是比较规则

        // 我们把map和sorted调换一下顺序，看起来就不那么别扭了
        List<String> result2 = list.stream()
                .filter(person -> person.getAge() > 18)
                .sorted(Comparator.comparing(Person::getName, String::compareTo).reversed())
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println(result2);

        // 为什么Comparator.comparing().reversed()可以链式调用呢？
        // 上面说了哦，因为Comparator.comparing()返回的还是Comparator对象~
    }

    /**
     * 所谓的skip(N)就是跳过前面N个元素，limit(N)就是只取N个元素。
     */
    @Test
    void testSkipAndLimit(){
        List<String> result = list.stream()
                .filter(person -> person.getAge() > 17)
                // peek()先不用管，它不会影响整个流程，就是打印看看filter操作后还剩什么元素
                .peek(person -> System.out.println(person.getName()))
                .skip(1L)
                .limit(2)
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println(result);
    }

    /**
     * 最常用的4个方法：Collectors.toList()、Collectors.toSet()、Collectors.toMap()、Collectors.joining()
     */
    @Test
    void testCollect(){

        // 最常用的4个方法

        // 把结果收集为List
        List<String> toList = list.stream().map(Person::getAddress).collect(Collectors.toList());
        System.out.println(toList);

        // 把结果收集为Set
        Set<String> toSet = list.stream().map(Person::getAddress).collect(Collectors.toSet());
        System.out.println(toSet);

        // 把结果收集为Map，前面的是key，后面的是value，如果你希望value是具体的某个字段，可以改为toMap(Person::getName, person -> person.getAge())
        Map<String, Person> nameToPersonMap = list.stream().collect(Collectors.toMap(Person::getName, person -> person));
        System.out.println(nameToPersonMap);

        // 把结果收集起来，并用指定分隔符拼接
        String result = list.stream().map(Person::getAddress).collect(Collectors.joining("~"));
        System.out.println(result);

    }

    @Test
    void testMaxAndMin(){
        // 匿名内部类的方式，实现Comparator，明确按什么规则比较（所谓最大，必然是在某种规则下的最值）
        Optional<Integer> maxAge = list.stream().map(Person::getAge).max(new Comparator<Integer>() {
            @Override
            public int compare(Integer age1, Integer age2) {
                return age1 - age2;
            }
        });
        System.out.println(maxAge.orElse(0));
        // 不用匿名类
        Optional<Integer> maxAge1 = list.stream().map(Person::getAge).max((age1,age2)->age1-age2);
        System.out.println(maxAge1.orElse(0));
        // 用comparator方法
        Optional<Integer> maxAge2 = list.stream().map(Person::getAge).max(Integer::compareTo);
        System.out.println(maxAge2.orElse(0));

    }

    /**
     * 测试去重
     */
    @Test
    void testDistinct(){
        long count = list.stream().map(Person::getAddress).distinct().count();
        System.out.println(count);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
     static class Person {
        private String name;
        private Integer age;
        private String address;
        private Double salary;
    }
}
